<template>
	<el-space>
		<el-button type="primary" @click="() => drawerVisible.statistics = true">
			<template #icon>
				<icon name="el-icon-DataAnalysis"></icon>
			</template>
			数据统计
		</el-button>
		<el-button-group>
			<el-button type="primary" @click="toggleDirection">
				<template #icon>
					<icon name="local-aliIntl-document-conversion"/>
				</template>
				图表方向：{{ diagramDirection === 'horizontal' ? '横向' : '纵向' }}
			</el-button>
			<el-button type="primary" @click="diagram?.zoomToFit()">
				<template #icon>
					<icon name="local-cus-exit-fullscreen"></icon>
				</template>
				显示全部
			</el-button>
		</el-button-group>
		<el-button type="primary" @click="saveAsImage">
			<template #icon>
				<icon name="fa fa-download"></icon>
			</template>
			保存为图片
		</el-button>
		<el-button type="primary" @click="showInstructions">
			<template #icon>
				<icon name="fa fa-question-circle"></icon>
			</template>
			说明
		</el-button>
	</el-space>

	<div v-loading="loading" class="container" element-loading-text="正在加载产线数据">
		<el-row :gutter="20" justify="space-around">
			<!-- 思维导图 -->
			<el-col v-loading="myDiagramLoading" :span="24" :xs="24">
				<div :id="'productionLineDiagram_' + productionLineDataInner.productionLineId" :style="{minHeight: diagramMaxHeight + 55 + 'px'}"
				     class="production-line-diagram"></div>
			</el-col>
		</el-row>

		<!-- 对话框（物品详情） -->
		<cus-item-dialog v-model:visible="dialogVisible.item" :item-id="itemIdDisplayed"/>

		<!-- 对话框（建筑详情） -->
		<cus-building-dialog v-model:visible="dialogVisible.building" :building-id="buildingIdDisplayed"/>

		<!-- 对话框（产线图说明） -->
		<el-dialog v-model="dialogVisible.instruction" :close-on-click-modal="false" :close-on-press-escape="true" append-to-body center class="cus-dialog"
		           destroy-on-close draggable title="产线图说明" width="35%">
			<el-space alignment="normal" direction="vertical">
				<el-text>1. 拖拽：按住鼠标左键</el-text>
				<el-text>2. 缩放：Ctrl + 鼠标滚轮，或点击鼠标中键在常规和缩放模式之间切换</el-text>
				<el-text>3. 点击物品或建筑图片可查看详细信息</el-text>
				<el-text>4. <span style="color: #D57616">橙色<span style="font-size: x-large">○</span></span>表示物品，<span style="color: #409EFF">蓝色<span
						style="font-size: x-large">□</span></span>表示生产建筑，<span style="color: #9370DB">紫色<span style="font-size: x-large">○</span></span>表示副产物
				</el-text>
				<el-text>5. <span style="color: #F55656">&nbsp;溢出&nbsp;</span>字样表示物品的速率已超过顶级传送带或管道的运力</el-text>
			</el-space>
			<template #footer>
				<el-button icon="el-icon-CircleClose" type="warning" @click="dialogVisible.instruction = false">
					关闭
				</el-button>
			</template>
		</el-dialog>
	</div>

	<!-- 抽屉（数据统计） -->
	<el-drawer v-model="drawerVisible.statistics" :show-close="false" append-to-body direction="rtl" size="35%">
		<template #header>
			<el-text style="font-size: var(--el-font-size-large); font-weight: bold">数据统计</el-text>
		</template>
		<div class="statistics">
			<el-collapse v-model="activeNames">
				<el-collapse-item v-if="productionLineDataInner!.statistics.basicMaterialList.length > 0" name="material" title="原料">
					<el-descriptions :column="1" border>
						<el-descriptions-item v-for="item in productionLineDataInner!.statistics.basicMaterialList" :key="item.itemId" label-align="center">
							<template #label>
								{{ item.itemName }}
							</template>
							<div class="cus-center">
								{{ item.quantityPerMinute }}/min
								<el-image :src="getImage(item.image!)" fit="contain" style="width: 50px; height: 50px;cursor: pointer;margin-left: 10px"
								          @click="showItemDetail(item.itemId!)"></el-image>
							</div>
						</el-descriptions-item>
					</el-descriptions>
				</el-collapse-item>
				<el-collapse-item v-if="productionLineDataInner!.statistics.byProductList.length > 0" name="byProduct" title="副产物">
					<el-descriptions :column="1" border>
						<el-descriptions-item v-for="byProduct in productionLineDataInner!.statistics.byProductList" :key="byProduct.itemId" label-align="center">
							<template #label>
								<span :style="{'text-decoration': byProduct.extra === 0 ? 'line-through' : undefined, color: byProduct.extra === 0 ? 'gray' : undefined}">{{
										byProduct.itemName
									}}</span>
							</template>
							<div class="cus-center">
								<span :style="{'text-decoration': byProduct.extra === 0 ? 'line-through' : undefined, color: byProduct.extra === 0 ? 'gray' : undefined}">{{
										byProduct.quantityPerMinute
									}}/min</span>
								<el-image :src="getImage(byProduct.image!)" fit="contain" style="width: 50px; height: 50px;cursor: pointer;margin-left: 10px"
								          @click="showItemDetail(byProduct.itemId!)"></el-image>
								<span v-if="byProduct.extra !== byProduct.quantityPerMinute && byProduct.extra === 0">（可作为原料，无剩余）</span>
								<span v-if="byProduct.extra !== byProduct.quantityPerMinute && byProduct.extra > 0">（可作为原料，剩余：{{ byProduct.extra }}/min）</span>
							</div>
						</el-descriptions-item>
					</el-descriptions>
				</el-collapse-item>
				<el-collapse-item v-if="productionLineDataInner!.statistics.productionLineBuildingList.length > 0" name="productionLineBuilding" title="建筑">
					<el-descriptions :column="1" border>
						<el-descriptions-item v-for="building in productionLineDataInner!.statistics.productionLineBuildingList" :key="building.buildingId"
						                      label-align="center">
							<template #label>
								{{ building.buildingName }}
							</template>
							<div class="cus-center">
								{{ building.count }}<span v-if="building.count && building.count !== building.countExact">（{{ building.countExact }}）</span>&nbsp;
								<el-image :src="getImage(building.image!)" fit="contain" style="width: 50px; height: 50px;cursor: pointer;margin-left: 10px"
								          @click="showBuildingDetail(building.buildingId!)"></el-image>
							</div>
						</el-descriptions-item>
					</el-descriptions>
				</el-collapse-item>
				<el-collapse-item name="power" title="电力">
					<el-descriptions :column="1" border>
						<el-descriptions-item label-align="center">
							<template #label>
								总耗电量
							</template>
							<div class="cus-center">
								{{ productionLineDataInner!.statistics.totalPower }} MW
							</div>
						</el-descriptions-item>
					</el-descriptions>
				</el-collapse-item>
				<el-collapse-item v-if="productionLineDataInner!.statistics.othersList && productionLineDataInner!.statistics.othersList.length > 0" name="others"
				                  title="其他">
					<el-descriptions :column="1" border>
						<el-descriptions-item v-for="statisticsVo in productionLineDataInner!.statistics.othersList" label-align="center">
							<template #label>
								{{ statisticsVo.label }}
							</template>
							<div class="cus-center">
								<span v-if="statisticsVo.prefix">{{ statisticsVo.prefix }}</span>
								{{ statisticsVo.value }}
								<span v-if="statisticsVo.suffix">{{ statisticsVo.suffix }}</span>
								<el-image v-if="statisticsVo.image" :src="getImage(statisticsVo.image!)" fit="contain"
								          style="width: 50px; height: 50px;cursor: pointer;margin-left: 10px"
								          @click="statisticsVo.itemId ? showItemDetail(statisticsVo.itemId!) : showBuildingDetail(statisticsVo.buildingId)"></el-image>
							</div>
						</el-descriptions-item>
					</el-descriptions>
				</el-collapse-item>
			</el-collapse>
		</div>
	</el-drawer>

</template>

<script lang="ts" name="ProductionLineDetail" setup>
import {getImage, numberToPercent, treeArrayToArray} from '@/utils/commonUtil'
import {computed, onActivated, onMounted, reactive, ref, watch} from 'vue'
import CusBuildingDialog from '@/components/cus-building-dialog.vue'
import CusItemDialog from '@/components/cus-item-dialog.vue'
import {onBeforeRouteLeave, useRoute} from 'vue-router'
import {getUserProductionByIdApi} from '@/api/gameTool/productionLine.api'
import * as go from 'gojs'
import {GraphObject, InputEvent, Point, TreeLayout} from 'gojs'
import {debounce, type DebouncedFunc} from 'lodash-es'
import {calcMaxHeightWithExtra} from '@/utils/layoutUtil'
import {useEventListener} from '@vueuse/core'
import Icon from '@/components/icon.vue'

const route = useRoute()

interface Props {
	productionLineData?: ProductionLineDataForDiagramTab
}

const props = withDefaults(defineProps<Props>(), {
	productionLineData: undefined
})

const loading = ref(false)

const productionLineDataInner = reactive<ProductionLineDataForDiagramTab>({
	productionLineId: 0,
	treeData: [],
	links: [],
	statistics: {
		basicMaterialList: [],
		byProductList: [],
		productionLineBuildingList: [],
		totalPower: 0,
		othersList: []
	}
})

const activeNames = ref(['material', 'byProduct', 'productionLineBuilding', 'power', 'others'])
const dialogVisible = reactive({
	item: false,
	building: false,
	instruction: false
})
const drawerVisible = reactive({
	statistics: false
})

watch(() => route.path, () => {
	for (const key in drawerVisible) {
		drawerVisible[<keyof typeof drawerVisible>key] = false
	}
})

const itemIdDisplayed = ref<number>()
const buildingIdDisplayed = ref<number>()

const $ = go.GraphObject.make
const myDiagramLoading = ref(true)
let diagram: go.Diagram

const diagramDirection = ref<'horizontal' | 'vertical'>('horizontal')
const diagramAngle = computed<number>(() => diagramDirection.value === 'horizontal' ? 0 : 90)

const buildNodeTemplate = () => {
	return new go.Node('Vertical')
			.add(new go.Panel('Auto', {margin: 5})
					.bind('visible', 'hideNode', val => !val)
					.add(new go.Shape('Ellipse', {fill: '#141414FF', stroke: '#D57616'}))
					.add(new go.Picture({width: 75, height: 75, click: handleClickItemImage, cursor: 'pointer'}).bind('source', 'image', getImage))
			)
			.add(new go.Panel('Vertical')
					.bind('visible', 'hideNode', val => !val)
					// 物品名称
					.add(new go.TextBlock({stroke: '#D57616'}).bind('text', 'itemName'))
					// 分钟数量
					.add(new go.TextBlock({stroke: '#D57616'}).bind('visible', 'quantityPerMinute', val => val > 0).bind('text', 'quantityPerMinuteStr'))
					.add($('TreeExpanderButton'))
			)
}

const buildLinkTemplate = () => {
	return new go.Link({
		routing: go.Link.AvoidsNodes,
		fromSpot: go.Spot.Default,
		toSpot: go.Spot.Default,
		fromEndSegmentLength: 40,
		corner: 10
	})
			// 连线
			.add(new go.Shape({stroke: '#D57616'}).bind('visible', 'hideLink', val => !val))
			// 箭头
			// .add(new go.Shape({toArrow: 'Standard', stroke: '#D57616', fill: '#D57616'}))
			// 图片和文字
			.add(new go.Panel('Vertical', {
						segmentIndex: 0,
						segmentFraction: 0,
						segmentOffset: new Point(diagramDirection.value === 'horizontal' ? 25 : -60, diagramDirection.value === 'horizontal' ? -5 : -70)
					})
							.bind('visible', 'showBuilding')
							// 建筑图片
							.add(new go.Panel('Auto', {margin: 5})
									.add(new go.Shape('RoundedRectangle', {fill: '#141414FF', stroke: '#409eff'}))
									.add(new go.Picture({width: 50, height: 50, click: handleClickBuildingImage, cursor: 'pointer'}).bind('source', 'image', getImage)))
							// 建筑名称
							.add(new go.TextBlock({stroke: '#409eff'}).bind('text', 'buildingName'))
							// 建筑数量
							.add(new go.TextBlock({stroke: '#409eff'}).bind('visible', 'count', val => val > 0).bind('text', 'buildingCountStr'))
							// 超频按钮
							.add($('Button', {
												isEnabled: false,
												margin: 2,
												'ButtonBorder.fill': '#909399',
												'ButtonBorder.stroke': '#909399',
												'_buttonFillOver': '#b8bbc1',
												'_buttonStrokeOver': '#b8bbc1',
												'_buttonFillPressed': '#b8bbc1',
												'_buttonStrokePressed': '#b8bbc1'
											},
											$(go.TextBlock, {text: '不超频'}).bind('text', 'overclock', val => val === 1 ? '不超频' : val < 1 ? `降频:${numberToPercent(val)}` : `超频:${numberToPercent(val)}`))
											.bind('visible', 'showOverclockable')
											.bind('ButtonBorder.fill', 'overclock', val => val > 1 ? 'rgba(255,255,0,0.8)' : val < 1 ? 'rgba(64,158,255,0.8)' : 'rgba(184,187,193,0.8)')
											.bind('ButtonBorder.stroke', 'overclock', val => val > 1 ? 'rgba(255,255,0,0.8)' : val < 1 ? 'rgba(64,158,255,0.8)' : 'rgba(184,187,193,0.8)')
											.bind('_buttonFillOver', 'overclock', val => val > 1 ? 'rgba(255,255,0,1)' : val < 1 ? 'rgba(64,158,255,1)' : 'rgba(184,187,193,1)')
											.bind('_buttonStrokeOver', 'overclock', val => val > 1 ? 'rgba(255,255,0,1)' : val < 1 ? 'rgba(64,158,255,1)' : 'rgba(184,187,193,1)')
											.bind('_buttonFillPressed', 'overclock', val => val > 1 ? 'rgba(255,255,0,1)' : val < 1 ? 'rgba(64,158,255,1)' : 'rgba(184,187,193,1)')
											.bind('_buttonStrokePressed', 'overclock', val => val > 1 ? 'rgba(255,255,0,1)' : val < 1 ? 'rgba(64,158,255,1)' : 'rgba(184,187,193,1)')
							)
							// 副产物
							.add(new go.Panel('Vertical', {
										itemTemplate:
												$(go.Panel, 'Vertical',
														$(go.Panel, 'Auto', {margin: 5},
																$(go.Shape, 'Ellipse', {fill: '#141414FF', stroke: '#9370DB'}),
																$(go.Picture, {width: 35, height: 35, click: handleClickItemImage, cursor: 'pointer'}).bind('source', 'image', getImage)
														),
														$(go.Panel, 'Vertical',
																$(go.TextBlock, {stroke: '#9370DB'}).bind('text', 'itemName'),
																$(go.TextBlock, {stroke: '#9370DB'}).bind('text', 'quantityPerMinuteStr')
														)
												)
									}).bind('itemArray', 'byProducts')
							)
			)
}

const diagramMaxHeight = ref(800)
const calcDiagramMaxHeight: DebouncedFunc<() => typeof calcDiagramMaxHeight> = debounce(() => {
	diagramMaxHeight.value = calcMaxHeightWithExtra(140)
	return calcDiagramMaxHeight
}, 100, {
	leading: true,
	trailing: false,
	maxWait: 100
})

const showItemDetail = (itemId: number) => {
	itemIdDisplayed.value = itemId
	dialogVisible.item = true
}

const showBuildingDetail = (buildingId: number) => {
	buildingIdDisplayed.value = buildingId
	dialogVisible.building = true
}

const handleClickItemImage = (e: InputEvent, thisObj: GraphObject) => {
	let obj: any = thisObj
	// 防止无限循环
	let step = 0
	do {
		obj = obj.panel
		step++
	} while (!obj.data && step < 100)
	const data = obj.data
	if (data.itemId) {
		showItemDetail(data.itemId)
	} else {
		throw Error('无法获取物品数据')
	}
}

const handleClickBuildingImage = (e: InputEvent, thisObj: GraphObject) => {
	let obj: any = thisObj
	// 防止无限循环
	let step = 0
	do {
		obj = obj.panel
		step++
	} while (!obj.data && step < 100)
	const data = obj.data
	if (data.buildingId) {
		showBuildingDetail(data.buildingId)
	} else {
		throw Error('无法获取建筑数据')
	}
}

const toggleDirection = () => {
	if (diagramDirection.value === 'horizontal') {
		diagramDirection.value = 'vertical'
	} else {
		diagramDirection.value = 'horizontal'
	}
	// 图表方向
	(<TreeLayout>diagram.layout).angle = diagramAngle.value
	// 连线模版
	diagram.linkTemplate = buildLinkTemplate()
	diagram.layoutDiagram()
}

const saveAsImage = () => {
	const imgData = diagram.makeImageData({
		returnType: 'image/png',
		scale: 1,
		maxSize: new go.Size(Infinity, Infinity)
	})
	// 创建一个链接元素来下载图像
	const link = document.createElement('a')
	link.href = <string>imgData
	link.download = `${productionLineDataInner.label.replaceAll('/min', ' per min')}.png`
	document.body.appendChild(link)
	link.click()
	document.body.removeChild(link)
}

const showInstructions = () => {
	dialogVisible.instruction = true
}

// 优先使用props中的产线数据，如果没有则根据路由参数中的生产线id查询产线数据
loading.value = true
if (props.productionLineData) {
	Object.assign(productionLineDataInner, props.productionLineData)
	loading.value = false
} else {
	const lineId = route.path.split('/').pop()
	if (!lineId) {
		throw Error('无法从路由中获取产线id')
	}
	const lineIdNumber = Number(lineId)
	if (lineIdNumber <= 0 || !Number.isSafeInteger(lineIdNumber)) {
		throw Error('产线id必须为正整数')
	}
	getUserProductionByIdApi(lineIdNumber).then(response => {
		const userProduction = response.data
		const parse = JSON.parse(userProduction.data!)
		productionLineDataInner.productionLineId = userProduction.id!
		productionLineDataInner.treeData = parse.treeData
		productionLineDataInner.links = parse.links
		productionLineDataInner.statistics = parse.statistics
		productionLineDataInner.productionLineId = userProduction.id!
	}).finally(() => {
		loading.value = false
	})
}

onMounted(() => {

	useEventListener(window, 'resize', calcDiagramMaxHeight()!)

	diagram = new go.Diagram('productionLineDiagram_' + productionLineDataInner.productionLineId)
	// 节点模版
	diagram.nodeTemplate = buildNodeTemplate()
	// 连线模版
	diagram.linkTemplate = buildLinkTemplate()
	// 布局
	const treeLayout = new go.TreeLayout()
	treeLayout.nodeSpacing = 125
	treeLayout.layerSpacing = 125
	diagram.layout = treeLayout
	// 禁止选中和拖拽
	diagram.allowSelect = false
	// 绑定数据
	diagram.model = new go.GraphLinksModel(treeArrayToArray(productionLineDataInner.treeData), productionLineDataInner.links)
	// 自动缩放
	// diagram.autoScale = go.Diagram.UniformToFill
	setTimeout(() => {
		// diagram.autoScale = go.Diagram.None
		diagram.focus()
		myDiagramLoading.value = false
	}, 200)
})

// 该页面开启缓存且首次或后续进入时执行，刷新页面不执行
onActivated(() => {
	useEventListener(window, 'resize', calcDiagramMaxHeight()!)
})

onBeforeRouteLeave(() => {
	window.removeEventListener('resize', calcDiagramMaxHeight)
})

</script>

<style lang="scss" scoped>
.production-line-diagram {
	width: 100%;
	scrollbar-width: none;
	-ms-overflow-style: none;

	::-webkit-scrollbar {
		display: none;
	}
}

.statistics {
	border-radius: var(--el-border-radius-base);
	box-shadow: var(--cus-box-shadow);
	padding: 10px;
}

.custom-tree-node {
	flex: 1;
	display: flex;
	align-items: center;
	justify-content: space-between;
	font-size: var(--el-font-size-base);
	padding-right: 8px;
}
</style>
